package com.lang.ref.reference;

import org.junit.Test;

import java.lang.ref.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Reference 类详解
 * 这个类是被 bootClassLoader 类加载器加载，所以在项目启动时就已经初始化了，此时启动了 ReferenceHandler 线程,且通过 lock.wait() 挂起线程 等待被 jvm 唤醒。
 * ReferenceHandler 线程
 *      1.当发生 GC 时会被唤醒，此时发现 pending 不等于null referent 等于 null 即对象被回收时( 网上教程为 当对象可达状态发生改变时则会被唤醒，且将 Reference对象放到 pending链表中 )。
 *      2.取 pending 链表中的第一个数据。
 */
public class ReferenceDemo {
    /**
     *   -verbose:gc -Xms40m -Xmx40m
     */
    public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        //是 被 bootClassLoader 类加载器加载的 且加载时已经被初始化了
        System.out.println(Reference.class.getClassLoader());

        byte[]  b = new byte[1024*1024*10];
        byte[]  b1 = new byte[1024*1024*10];
        ReferenceQueue rq = new ReferenceQueue();
        System.out.println(rq);
        WeakReference sr = new WeakReference(b, rq);
        WeakReference sr1 = new WeakReference(b1, rq);
        WeakReference sr2 = new WeakReference(b1, rq);
        System.out.println(sr);
        System.out.println(sr1);


        System.out.println();
        Field referent = getField("referent");
        Field pending = getField("pending");
        Field next = getField("next");
        Field discovered = getField("discovered");
        Field queue = getField("queue");


        Thread.sleep(1000);
        System.out.println(discovered.get(sr));
        System.out.println(discovered.get(sr1));
        System.out.println(discovered.get(sr2));
        System.out.println();

        System.out.println("referent "+(referent.get(sr)==null?"is null ":"not null" ) +" "+referent.get(sr));
        System.out.println("pending "+(pending.get(sr)==null?"is null ":"not null")+" "+pending.get(sr));
        System.out.println("next "+(next.get(sr)==null?"is null ":"not null") +" "+pending.get(sr));
        System.out.println("discovered "+(discovered.get(sr)==null?"is null ":"not null") +" "+discovered.get(sr));
        System.out.println("queue "+(queue.get(sr)==null?"is null ":"not null") +" "+queue.get(sr));
        System.out.println();


        b =null;
        System.gc();
        Thread.sleep(100);
        System.out.println("----------------");
//        System.gc();
//        Thread.sleep(100);

        System.out.println("referent "+(referent.get(sr)==null?"is null ":"not null" ) +" "+referent.get(sr));
        System.out.println("pending "+(pending.get(sr)==null?"is null ":"not null")+" "+pending.get(sr));
        System.out.println("next "+(next.get(sr)==null?"is null ":"not null") +" "+next.get(sr));
        System.out.println("discovered "+(discovered.get(sr)==null?"is null ":"not null") +" "+discovered.get(sr));
        System.out.println("queue "+(queue.get(sr)==null?"is null ":"not null") +" "+queue.get(sr));
    }


    public static Field  getField(String fieldName) throws NoSuchFieldException {
        Field field = Reference.class.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field;
    }


    public static Set<Reference> testSet = new HashSet<>();
    /**
     * 改变对象的可达性，将一个对象使用四种引用，修改对象的可达性，将对象可达性依次降为 强-> 软-> 虚 ->幻影
     * -verbose:gc
     * 操作过程：
     *      在 java.lang.ref.Reference#tryHandlePending(boolean)#191  中的 lock.wait(); 后面语句打上断点 表示 GC 通知ReferenceHandler线程
     *      并在 D:/java-1.8.0-openjdk/src.zip!/java/lang/ref/Reference.java:179 中 打上断点 断点条件为 ReferenceDemo.testSet.contains(pending)
     *      并在 该方法的每个 【开始去掉*引用前打上断点】 debug
     *
     *  当强引用去掉时，并强制gc 此时，gc会发现对象的可达性发生改变，此时 JVM 会 lock.notify 调用 ReferenceHandler 线程，修改对象引用状态。
     *  且发现当虚拟机内存够用时，gc 并不会回收 SoftReference 对象。
     *
     */
    @Test
    public void testkdx() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        ReferenceQueue q = new ReferenceQueue();
        Field pending = getField("pending");
        //强引用
        byte[]  b = new byte[1024*1024*10];//直接使用强引用 在内存不足时会出现 OutOfMemoryError 导致虚拟机挂掉

        //软引用
        SoftReference sr = new SoftReference(b, q);

        //弱引用
        WeakReference wr = new WeakReference(b,q);

        //虚(幻影)引用
        PhantomReference pr = new PhantomReference(b, q);


        testSet.add(sr);
        testSet.add(wr);
        testSet.add(pr);

        Thread.currentThread().sleep(100);

        System.out.println("开始去掉强引用");
        b = null;
        System.out.println("结束去掉强引用");
        Thread.currentThread().sleep(100);
        System.out.println();

        System.gc();
        Thread.sleep(100);

        System.out.println("开始去掉软引用");
        System.out.println(sr.get());//gc时不会回收
        sr.clear();
        System.out.println(sr.get());
        System.out.println("结束去掉软引用");
        Thread.currentThread().sleep(100);
        System.out.println();

        System.gc();
        Thread.sleep(100);

        System.out.println("开始去掉弱引用");
        System.out.println(wr.get());//在第一次gc时就回收了
        wr.clear();
        System.out.println(wr.get());
        System.out.println("结束去掉弱引用");
        Thread.currentThread().sleep(100);
        System.out.println();

        System.gc();

        System.out.println("开始去掉虚(幻影)引用");
        System.out.println(pr.get());
        pr.clear();
        System.out.println(pr.get());
        System.out.println("结束去掉虚(幻影)引用");
        Thread.currentThread().sleep(100);
    }
}
